package com.aizuda.easyManagerTool.service.terminal.impl;


import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.aizuda.easy.security.domain.Rep;
import com.aizuda.easy.security.domain.Req;
import com.aizuda.easy.security.server.EasySecurityServer;
import com.aizuda.easyManagerTool.domain.dto.terminal.SSHInfoDTO;
import com.aizuda.easyManagerTool.domain.dto.terminal.SSHMessageDTO;
import com.aizuda.easyManagerTool.domain.tuple.Tuple2;
import com.aizuda.easyManagerTool.domain.tuple.Tuple4;
import com.aizuda.easyManagerTool.domain.vo.server.ServerCompleteVO;
import com.aizuda.easyManagerTool.domain.vo.setting.SettingUserVO;
import com.aizuda.easyManagerTool.domain.vo.socket.SocketMessageVO;
import com.aizuda.easyManagerTool.domain.vo.terminal.MonitorVO;
import com.aizuda.easyManagerTool.mapper.server.ServerMapper;
import com.aizuda.easyManagerTool.service.socket.impl.SocketSessionManager;
import com.aizuda.easyManagerTool.service.terminal.MonitorTerminalService;
import com.aizuda.easyManagerTool.util.SSHManagerUtil;
import com.jcraft.jsch.ChannelExec;
import com.jcraft.jsch.Session;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;

import javax.annotation.Resource;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;
import java.util.stream.Collectors;

@Service("monitor")
@Slf4j
public class MonitorServiceImpl extends AbstractSSHTemplate implements MonitorTerminalService {
    SocketMessageVO<MonitorVO> socketMessageVO = new SocketMessageVO<MonitorVO>();
    /**
     * 命令集
     */
    @Resource
    EasySecurityServer easySecurityServer;
    @Resource
    ServerMapper serverMapper;


    @Override
    public Rep<String> monitor(Req<SSHMessageDTO, SettingUserVO> req) {
        SSHMessageDTO data = req.getData();
        try {
            SettingUserVO authUser = (SettingUserVO) easySecurityServer.getAuthUser(data.getCommand().substring(0,data.getCommand().indexOf(":")));
            String token = authUser.getId()+":"+authUser.getTenantId()+data.getCommand().substring(data.getCommand().indexOf(":"));
            WebSocketSession webSocketSession = SocketSessionManager.get(token);
            TerminalSessionManager.Manager manager = TerminalSessionManager.setGet(data.getCommand()+":monitor");
            ServerCompleteVO serverCompleteVO = serverMapper.findById(data.getServerId());
            BeanUtil.copyProperties(serverCompleteVO,data.getInfo());
            executeCommand(manager,data,webSocketSession);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return Rep.ok();
    }

    @Override
    public void onClose(String token, WebSocketSession session, CloseStatus closeStatus) throws IOException {
        TerminalSessionManager.Manager manager = TerminalSessionManager.setGet(token+"-monitor");
        closeEvent(manager);
    }



    @Override
    public MonitorVO monitor(Integer id){
        MonitorVO monitorVO = new MonitorVO();
        TerminalSessionManager.Manager manager = SessionManager.put(id.toString(), new TerminalSessionManager.Manager());
        try {
            ServerCompleteVO serverCompleteVO = serverMapper.findById(id);
            String[] command = Strategy.command.get(serverCompleteVO.getServerTypeId());
            SSHMessageDTO sshMessageDTO = new SSHMessageDTO();
            sshMessageDTO.setInfo(new SSHInfoDTO());
            sshMessageDTO.setTypeId(serverCompleteVO.getServerTypeId());
            BeanUtil.copyProperties(serverCompleteVO, sshMessageDTO.getInfo());
            // 去执行命令并获得结果
            execCommand(command,manager, sshMessageDTO,monitorVO);
        }catch (Exception e){
            log.error("数据源-SSH-连接失败,方法：{} {}","monitor",e.getMessage());
        }
        return monitorVO;
    }

    private void executeCommand(TerminalSessionManager.Manager manager,SSHMessageDTO data,WebSocketSession webSocketSession){
        Thread thread = new Thread(() -> {
            SocketMessageVO<MonitorVO> messageVO = BeanUtil.copyProperties(socketMessageVO, SocketMessageVO.class);
            messageVO.setType("monitor");
            manager.setThread(Thread.currentThread());
            MonitorVO monitorVO = new MonitorVO();
            try {
                String[] command = Strategy.command.get(data.getTypeId());
                while (true) {
                    if (ObjectUtil.isEmpty(webSocketSession)) {
                        log.debug("executeCommand session 退出中断");
                        return;
                    }
                    monitorVO.getDiskList().clear();
                    // 发送命令
                    execCommand(command, manager, data,monitorVO);
                    messageVO.setObj(monitorVO);
                    webSocketSession.sendMessage(new TextMessage(JSONUtil.toJsonStr(messageVO)));
                    Thread.sleep(3000);
                }
            } catch (Exception e) {

            } finally {
                closeEvent(manager);
                manager.setChannelExec(null);
                manager.setSession(null);
            }
        });
        thread.setName("终端监控-"+data.getInfo().getServerName());
        thread.start();
    }

    private void execCommand(String[] command,TerminalSessionManager.Manager manager,SSHMessageDTO data,MonitorVO monitorVO) throws Exception{
        Session session = SSHManagerUtil.setSession(manager, data.getInfo());
        Arrays.stream(command).parallel().forEach(i -> {
            ChannelExec channelExec = null;
            try {
                channelExec = SSHManagerUtil.setChannelExec(session, manager, i);
                byte[] buffer = new byte[1024];
                int k = 0;
                InputStream in = channelExec.getInputStream();
                StringBuffer sb = new StringBuffer();
                while ((k = in.read(buffer)) != -1) {
                    String str = new String(Arrays.copyOfRange(buffer, 0, k), StandardCharsets.UTF_8);
                    sb.append(str);
                }
                Tuple4<String[], String, String, MonitorVO> tuple4 = new Tuple4(command,i, sb.toString(), monitorVO);
                Strategy.method.get(data.getTypeId()).accept(tuple4);
                sb = null;
            }catch (Exception e){
                log.error("数据源-SSH-连接失败,方法：{} {}","execCommand",e.getMessage());
            }finally {
                if(channelExec != null) {
                    channelExec.disconnect();
                }
            }
        });
    }

    /**
     * 维护策略
     */
    public static class Strategy{
        public static Map<Integer,String[]> command = new HashMap<Integer,String[]>(){{
            //linux
            put(1,new String[]{
                    //CPU 使用情况
                    "top -b -n 1 | grep '%Cpu'",
                    //磁盘使用情况
                    "df",
                    //内存使用情况
                    "free"
            });
            // windows
            put(2,  new String[]{
                    // CPU 使用情况
                    "wmic cpu get loadpercentage",
                    //磁盘使用情况
                    "wmic logicaldisk get caption, freespace, size",
                    //内存使用情况
                    "wmic OS get FreePhysicalMemory, TotalVisibleMemorySize"
            });
        }};

        public static Map<Integer, Consumer<Tuple4<String[],String,String,MonitorVO>>> method = new HashMap<Integer,Consumer<Tuple4<String[],String,String,MonitorVO>>>(){{
            put(1,(tuple4) -> Strategy.analysisLinux(tuple4.f0,tuple4.f1,tuple4.f2,tuple4.f3));
            put(2,(tuple4) -> Strategy.analysisWindows(tuple4.f0,tuple4.f1,tuple4.f2,tuple4.f3));
        }};

        public static void analysisLinux(String[] command,String com,String str,MonitorVO monitorVO){
            try {
                if (com.equals(command[0])) {
                    List<Float> collect = Arrays.asList(str
                                    .replace("%Cpu(s):", "")
                                    .split(","))
                            .stream().filter(item-> StrUtil.isNotEmpty(item.trim())).map(item -> item.substring(0, item.lastIndexOf(" ")).trim())
                            .map(Float::new).collect(Collectors.toList());
                    monitorVO.setCpu(collect.get(3));
                }
                if (com.equals(command[1])) {
                    Arrays.stream(str.split("\n"))
                            .skip(1)
                            .forEach(item -> {
                                List<String> collect = Arrays.stream(item.split(" "))
                                        .filter(ik -> StrUtil.isNotEmpty(ik))
                                        .limit(3)
                                        .collect(Collectors.toList());
                                MonitorVO.Disk disk = new MonitorVO.Disk();
                                disk.setName(collect.get(0));
                                disk.setSize(Long.valueOf(collect.get(1)));
                                disk.setUse(Long.valueOf(collect.get(2)));
                                monitorVO.getDiskList().add(disk);
                            });
                }
                if (com.equals(command[2])) {
                    List<Long> collect = Arrays.stream(str.substring(str.indexOf("\n"), str.lastIndexOf("\n"))
                                    .trim()
                                    .replace("Mem:", "")
                                    .replace("Swap:", "")
                                    .replace("", "")
                                    .replace("\r\n","")
                                    .replace("\n","")
                                    .replace("\r","")
                                    .trim().split(" "))
                            .filter(item -> StrUtil.isNotEmpty(item.trim()))
                            .map(Long::new).collect(Collectors.toList());
                    monitorVO.setMemoryTotal(collect.get(0));
                    monitorVO.setMemoryUse(collect.get(1));
                }
            }catch (Exception e){
                log.error("Linux终端监控数据解析异常 {}",e.getMessage());
            }
        }

        public static void analysisWindows(String[] command,String com,String str,MonitorVO monitorVO){
            try {
                if (com.equals(command[0])) {
                    Float aFloat = Arrays.stream(str.split("\n")).limit(2).skip(1).map(item -> {
                        String replace = item.replace("\n", "").trim();
                        if(StrUtil.isEmpty(replace)){
                            return 0F;
                        }
                        return Float.valueOf(replace);
                    }).findAny().orElse(0F);
                    monitorVO.setCpu(100 - aFloat);
                }
                if (com.equals(command[1])) {
                    Arrays.stream(str.split("\n")).skip(1).map(item ->
                            Arrays.stream(item.replace("\n", "").split(" "))
                                    .filter(StrUtil::isNotEmpty)
                                    .map(String::trim)
                                    .collect(Collectors.toList())
                    ).filter(item -> item.size() > 2).forEach(item -> {
                        MonitorVO.Disk disk = new MonitorVO.Disk();
                        disk.setName(item.get(0));
                        disk.setSize(Long.valueOf(item.get(2)));
                        disk.setUse(disk.getSize() - Long.valueOf(item.get(1)));
                        monitorVO.getDiskList().add(disk);
                    });
                }
                if (com.equals(command[2])) {
                    Arrays.stream(str.split("\n")).skip(1)
                            .map(item -> {
                                String replace = item.replace("\n", "");
                                return Arrays.stream(replace.split(" "))
                                        .filter(StrUtil::isNotEmpty)
                                        .map(String::trim)
                                        .collect(Collectors.toList());
                            }).filter(item -> item.size()>1)
                            .forEach(item -> {
                                monitorVO.setMemoryTotal(Long.valueOf(item.get(1)));
                                monitorVO.setMemoryUse(monitorVO.getMemoryTotal() - Long.valueOf(item.get(0)));
                            });
                }
            }catch (Exception e){
                log.error("Windows终端监控数据解析异常 {}",e.getMessage());
            }
        }
    }

    /**
     * 维护缓存jsch Session
     */
    public static class SessionManager{
        private static Map<String, Tuple2<Long, TerminalSessionManager.Manager>> sessionManager = new ConcurrentHashMap<>();

        static {
            Thread thread = new Thread(() -> check());
            thread.setName("检查缓存session失效-");
            thread.start();
        }

        public static TerminalSessionManager.Manager put(String key,TerminalSessionManager.Manager manager){
            Tuple2<Long, TerminalSessionManager.Manager> longSessionTuple2 = sessionManager
                    .computeIfAbsent(key, (k) -> new Tuple2<Long, TerminalSessionManager.Manager>(System.currentTimeMillis()+20*1000,manager));
            longSessionTuple2.f0 = System.currentTimeMillis()+20*1000;
            return longSessionTuple2.f1;
        }

        public static void check(){
            while(true) {
                try {
                    Thread.sleep(3000);
                    sessionManager.entrySet().removeIf(next -> {
                        Tuple2<Long, TerminalSessionManager.Manager> longSessionTuple2 = next.getValue();
                        if (longSessionTuple2.f0 > System.currentTimeMillis()) {
                            return false;
                        }
                        longSessionTuple2.f1.getSession().disconnect();
                        log.debug("SessionManager缓存清除 {}",next.getKey());
                        return true;
                    });
                }catch (Exception e){}
            }
        }

    }

}
